﻿using System.Collections.Generic;
using System.Drawing.Imaging;
using Mido.Properties;
using Mido.UiModules.SettingsModule;
using Mido.Utils.Logging;
using Mido.Utils.Mvvm;
using Mido.Utils.Watermark;
using System;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Point = System.Windows.Point;
using Size = System.Drawing.Size;

namespace Mido.Domain
{
	public class ProcessedImage
	{
		internal delegate void ProcessedImageDelegate(ProcessedImage image);

	    internal event ThreadStart CustomPositionChanged;
		internal event ThreadStart RotateAngleChanged;
		internal event ThreadStart SmallImageChanged;
		internal event ProcessedImageDelegate ExcludeCall;

	    private readonly SettingsModel _settings;
        private readonly string _imageUri;
	    private readonly FileInfo _fileInfo;
        private readonly Size _size;
		
        private WeakReference _fullImage;
		private BitmapSource _smallImage;
        
	    private Point? _customPosition;
	    private WatermarkAngle _rotateAngle = WatermarkAngle.a0;

		public ProcessedImage(string imageUri, SettingsModel settings)
		{
		    if (string.IsNullOrEmpty(imageUri)) 
                throw new ArgumentNullException("imageUri");
		    if (settings == null)
                throw new ArgumentNullException("settings");

		    _imageUri = imageUri;
		    _settings = settings;
			_fileInfo = new FileInfo(imageUri);
            _size = WatermarkHelper.GetImageSize(_imageUri);

			ProcessedImageUpdated.Add(this);
		}

		internal void ReloadFullImage()
		{
			_fullImage = null;
		}

        internal Point? CustomPosition
	    {
	        get { return _customPosition; }
            set
            {
                if (_customPosition == value)
                    return;

                _customPosition = value;

                if (CustomPositionChanged != null)
                    CustomPositionChanged();
            }
	    }

		internal void OnExcludeCall()
		{
			if (ExcludeCall != null)
				ExcludeCall(this);
		}

	    internal void SaveWatermarkedImageTo(string outputPath, ImageFormat format)
        {
            if (string.IsNullOrEmpty(outputPath)) 
                throw new ArgumentNullException("outputPath");

            if (_fullImage == null || !_fullImage.IsAlive)
                InitializeFullImage();

            DrawingGroup drawingVisual = new DrawingGroup();
            DrawingContext context = drawingVisual.Open();
            context.DrawImage(FullImage, new Rect(0, 0, FullImage.PixelWidth, FullImage.PixelHeight));
            WatermarkHelper.OnRender(this, context);
            context.Close();

            WatermarkHelper.SaveWatermarkedImageTo(new DrawingImage(drawingVisual), outputPath, format);
        }

	    public string ToolTipText
		{
			get
			{
				decimal size = _fileInfo.Length;
				byte sizeEnum = 0;

				while (size >= 1024)
				{
					size /= 1024;
					sizeEnum++;
				}

				string sizeString = string.Format("{0:F1} {1}", size,
					sizeEnum == 0 ? Resources.ProcessedImage_ToolTipText__bytes :
					sizeEnum == 1 ? "Kb" :
					sizeEnum == 2 ? "Mb" :
					sizeEnum == 3 ? "Gb" :
					sizeEnum == 4 ? "Tb" : string.Empty);

				return
					string.Format(Resources.ProcessedImage_ToolTipText_Location___0_, _imageUri) + Environment.NewLine +
					string.Format(Resources.ProcessedImage_ToolTipText_Size___0_, sizeString) + Environment.NewLine +
					string.Format(Resources.ProcessedImage_ToolTipText_Resolution___0___1_, _size.Width, _size.Height);
			}
		}

	    public SettingsModel Settings
	    {
	        get { return _settings; }
	    }

        public WatermarkAngle RotateAngle
	    {
	        get { return _rotateAngle; }
	        set
	        {
                if (_rotateAngle == value)
                    return;

	            _rotateAngle = value;

	            ReloadFullImage();
				InitializeSmallImage();

				if (RotateAngleChanged != null)
					RotateAngleChanged();
	        }
	    }

	    public BitmapSource FullImage
	    {
	        get
	        {
                if (_fullImage == null || !_fullImage.IsAlive)
                    InitializeFullImage();

				return (BitmapSource)_fullImage.Target;
	        }
	    }

	    public BitmapSource SmallImage
	    {
	        get { return _smallImage ?? new BitmapImage(); }
	    }

	    private void InitializeFullImage()
	    {
            Size imageSourceSize = _rotateAngle == WatermarkAngle.a90 || _rotateAngle == WatermarkAngle.a270 ? new Size(_size.Height, _size.Width) : _size;
            Size newSize = _settings.KeepOriginalSize ? imageSourceSize : WatermarkHelper.CalculateNewSize(imageSourceSize, _settings);

	        byte[] imageBytes = File.ReadAllBytes(_imageUri);
	        
			_fullImage = new WeakReference(WatermarkHelper.CreateImage(imageBytes, _rotateAngle, newSize.Width, newSize.Height));
	    }

	    internal void InitializeSmallImage()
	    {
            Size newSize = WatermarkHelper.CalculateNewSize(_size, new SettingsModel { MaxHeight = 64, MaxWidth = 64 });
			BitmapImage result = WatermarkHelper.CreateThumbnailImage(_imageUri, newSize.Width, newSize.Height);

			double angle =
				RotateAngle == WatermarkAngle.a90 ? 90 :
				RotateAngle == WatermarkAngle.a180 ? 180 :
				RotateAngle == WatermarkAngle.a270 ? 270 : 0;

			_smallImage = new TransformedBitmap(result, new RotateTransform(angle, 0.5, 0.5));
			_smallImage.Freeze();

			if (SmallImageChanged != null)
				SmallImageChanged();
	    }

	    public Size Size
	    {
	        get { return _size; }
	    }

	    public FileInfo FileInfo
	    {
	        get { return _fileInfo; }
	    }
	}

	internal static class ProcessedImageUpdated
	{
		private static readonly Queue<ProcessedImage> _images = new Queue<ProcessedImage>();
		private static readonly object _sync = new object();
		private static readonly AutoResetEvent _event = new AutoResetEvent(false);

		static ProcessedImageUpdated()
		{
			Thread worker = new Thread(OnWork);
			worker.SetApartmentState(ApartmentState.STA);
			worker.IsBackground = true;
			worker.Start();
		}

		internal static void Add(ProcessedImage image)
		{
			lock(_sync)
				_images.Enqueue(image);

			_event.Set();
		}

		private static void OnWork()
		{
			try
			{
				while (true)
				{
					int count;

					lock (_sync)
						count = _images.Count;

					if (count == 0)
						_event.WaitOne();

					_event.Reset();

					ProcessedImage image;

					lock (_sync)
						image = _images.Dequeue();

					image.InitializeSmallImage();
				}
			}
			catch (ThreadAbortException)
			{
				// nothing...
			}
			catch(Exception exception)
			{
				Logger.Log(exception);
			}
		}
	}
}
